/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file i2c_drv.c
 * @brief This file defines i2c driver functions
 */

/*******Includes***************************************************************/
#include "i2c_reg_access.h"
#include "clock_drv.h"

/*******Definitions************************************************************/
#define I2C_HW_DEADLINE_TIMEOUT (0x8000UL)

/*! @brief Direction of a I2C transfer - transmit or receive */
typedef enum {
    I2C_TX_REQ = 0U,    /*!< I2C transmit transfer request */
    I2C_RX_REQ = 1U     /*!< I2C receive transfer  request */
} i2c_transfer_direction_t;

/*! @brief DMA internal parameters structure */
typedef struct {
    uint8_t dmaChannel;                             /* Channel number for the DMA channel */
    dma_transfer_type_t dmaTransferType;            /* Type for the DMA transfer */
    uint32_t i2cDataRegAddr;                        /* I2C data register address */
    uint32_t bufferAddress;                         /* Buffer used for transfer */
    uint32_t transferSize;                          /* Size of the data to be transfered */
    i2c_transfer_direction_t transferDirection;     /* specify if the driver will make a receive or transmit DMA transfer */
    dma_perial_request_t request;                   /* specify dma request number */
} i2c_dma_transfer_t;

/*******Variables**************************************************************/
/*! @brief Table to save register base addresses for i2c instances */
static I2C_Type* const s_i2cRegBase[CONFIG_I2C_INSTANCE_COUNT] = CONFIG_I2C_REG_BASE_PTRS;

/*! @brief Table to save i2c irq numbers */
static const IRQn_Type s_i2cIrqId[CONFIG_I2C_INSTANCE_COUNT] = CONFIG_I2C_IRQS;

/*! @brief Table to save i2c clock names */
static const clk_names_t s_i2cClkName[CONFIG_I2C_INSTANCE_COUNT] = CONFIG_I2C_CLOCK_NAMES;

/*! @brief Table to save i2c module names */
static const module_names_t s_i2cModuleName[CONFIG_I2C_INSTANCE_COUNT] = CONFIG_I2C_MODULE_NAMES;

/*! @brief Table to i2c rx dma request */
static const dma_perial_request_t s_i2cRxDmaRequest[CONFIG_I2C_INSTANCE_COUNT] = CONFIG_I2C_RX_DMA_REQS;

/*! @brief Table of I2C tx dma request for */
static const dma_perial_request_t s_i2cTxDmaRequest[CONFIG_I2C_INSTANCE_COUNT] = CONFIG_I2C_TX_DMA_REQS;

/*! @brief Pointer to runtime state structure */
static i2c_master_state_t* s_i2cMasterState[CONFIG_I2C_INSTANCE_COUNT] = {NULL, NULL};
static i2c_slave_state_t* s_i2cSlaveState[CONFIG_I2C_INSTANCE_COUNT] = {NULL, NULL};

/*! @brief variate to save i2c slave transfer direction */
static uint8_t s_slaveDirection = I2C_SLAVE_RECEIVE;

/*! @brief variate to save i2c dma transfer information */
static i2c_dma_transfer_t s_i2cDmaTransferParam[CONFIG_I2C_INSTANCE_COUNT];

/*******Prototypes*************************************************************/


/*******Private Functions******************************************************/
static errcode_t I2C_MasterWaitTransferEnd(uint32_t instance, uint32_t timeout);
static void I2C_MasterStartDmaTransfer(uint32_t instance);
static errcode_t I2C_MasterSendAddress(I2C_Type* regBase, i2c_master_state_t* master, bool receive);
static void I2C_MasterEndTransfer(I2C_Type* regBase, i2c_master_state_t* master, bool sendStop);
static void I2C_MasterHandleTransmitData(uint32_t instance, i2c_master_state_t* master);
static void I2C_MasterHandleReceiveData(uint32_t instance, i2c_master_state_t* master);
static void I2C_MasterIntEndTransfer(uint32_t instance, i2c_master_state_t* master,
                                     errcode_t errCode, i2c_event_t event);
static void I2C_MasterIRQProcess(uint32_t instance);
static void I2C_ConfigureDmaTransfer(uint32_t instance, i2c_dma_transfer_t* dmaTransferParams);
static errcode_t I2C_SendStartSignal(I2C_Type* regBase);
static errcode_t I2C_StartReceiveCmd(uint32_t instance, uint8_t nackType);
static errcode_t I2C_SendOneByteData(I2C_Type* regBase, uint8_t data);
static errcode_t I2C_SendStopSignal(I2C_Type* regBase);
static errcode_t I2C_ReceiveOneByteData(I2C_Type* regBase, uint8_t* data, uint8_t nackType);
static errcode_t I2C_ReceiveLastOneByteData(I2C_Type* regBase, uint8_t* data);
static void I2C_SlaveStartDmaTransfer(uint32_t instance);
static errcode_t I2C_SlaveWaitTransferEnd(uint32_t instance, uint32_t timeout);
static void I2C_SlaveEndTransfer(I2C_Type* regBase, i2c_slave_state_t* slave);
static void I2C_SlaveIRQProcess(uint32_t instance);
static void I2C_SlaveHandleTransmitData(uint32_t instance, i2c_slave_state_t* slave);
static void I2C_SlaveHandleReceiveData(uint32_t instance, i2c_slave_state_t* slave);
static void I2C_SlaveIntEndTransfer(uint32_t instance, i2c_slave_state_t* slave,
                                    errcode_t errCode, i2c_event_t event);
static void I2C_MasterDmaCallback(uint8_t channel, dma_event_t event, void* parameter);
static void I2C_SlaveDmaCallback(uint8_t channel, dma_event_t event, void* parameter);
static errcode_t I2C_CheckSlaveBuffer(i2c_slave_state_t* slave);
/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : I2C_MasterInit
 *** Description   : Initializes the I2C driver in master mode
 ******************************************************************************/
void I2C_MasterInit(uint32_t instance, i2c_master_state_t* master, const i2c_master_config_t* i2cConfig)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(master != NULL);
    ASSERT_PARAM(i2cConfig != NULL);

    /* Check to see if the I2C master instance is already initialized */
    ASSERT_PARAM(s_i2cMasterState[instance] == NULL);

    I2C_Type* regBase;
    errcode_t errCode = ERR_SUCCESS;
    module_names_t i2cModuleName;

    regBase = s_i2cRegBase[instance];
    s_i2cMasterState[instance] = master;
    i2cModuleName = s_i2cModuleName[instance];

    /* Set i2c bus clock enable */
    CLOCK_SetModuleBusClockEnable(i2cModuleName, true);
    /* Set i2c instance in-active */
    CLOCK_SetModuleSoftResetState(i2cModuleName, RESET_INACTIVE);

    /* Initialize driver status structure */
    master->rxBuffer = NULL;
    master->rxSize = 0U;
    master->txBuffer = NULL;
    master->txSize = 0U;
    master->errCode = ERR_SUCCESS;
    master->i2cIdle = true;
    master->slaveAddress = i2cConfig->slaveAddress;
    master->is10bitAddr = i2cConfig->is10bitAddr;
    master->transferType = i2cConfig->transferType;
    master->callback = i2cConfig->masterCallback;
    master->callbackParam = i2cConfig->callbackParam;
    master->blocking = false;

    /* Store DMA channel number used in transfer */
    master->dmaChannel = i2cConfig->dmaChannel;

    /* Initialize the semaphore */
    errCode = OS_Adapter_CreateSemaphore(&(master->idleSemaphore), 0U);
    ASSERT_PARAM(errCode == ERR_SUCCESS);
    (void)errCode;

    /* Enable i2c interrupt */
    NVIC_EnableIRQ(s_i2cIrqId[instance]);

    /* Initialize module */
    I2C_REG_SoftwareReset(regBase);
    I2C_REG_SetMode(regBase, I2C_MODE_MASTER);
    I2C_REG_SetBaudrate(regBase, i2cConfig->baud.i2cBaudPrescaler, i2cConfig->baud.i2cBaudSampleWidth);
    I2C_REG_SetClkSyncEnable(regBase, i2cConfig->isMasterSclSyc);
    I2C_REG_SetMasterArbEnable(regBase, i2cConfig->isMasterArb);
    I2C_REG_SetDeglitchCnt(regBase, i2cConfig->glitchFilterCnt);

    /* Set slave address */
    I2C_MasterSetSlaveAddr(instance, i2cConfig->slaveAddress, i2cConfig->is10bitAddr);

    /* Enable I2C Module */
    I2C_REG_SetModuleEnable(regBase, true);
    OS_Adapter_Init(NULL);
}

/*******************************************************************************
 *** Function Name : I2C_MasterDeinit
 *** Description   : Deinitializes the I2C driver in master mode
 ******************************************************************************/
void I2C_MasterDeinit(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    module_names_t i2cModName;
    IRQn_Type i2cIrq;

    i2cModName = s_i2cModuleName[instance];
    i2cIrq = s_i2cIrqId[instance];

    /* Set i2c module active */
    CLOCK_SetModuleSoftResetState(i2cModName, RESET_ACTIVE);

    /* Destroy the semaphore */
    if (s_i2cMasterState[instance] != NULL) {
        (void)OS_Adapter_DestroySemaphore(&(s_i2cMasterState[instance]->idleSemaphore));
    }

    /* Disable i2c module clock and irq*/
    CLOCK_SetModuleBusClockEnable(i2cModName, false);
    NVIC_DisableIRQ(i2cIrq);
    NVIC_ClearPendingIRQ(i2cIrq);

    s_i2cMasterState[instance] = NULL;
}

/*******************************************************************************
 *** Function Name : I2C_MasterGetBaudRate
 *** Description   : Rteurn the currently configured baud rate
 ******************************************************************************/
void I2C_MasterGetBaudRate(uint32_t instance, uint32_t* baudrate)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(baudrate != NULL);

    I2C_Type* regBase;
    uint8_t prescaler;
    uint8_t sampleWidth;
    clk_names_t i2cClockName;
    uint32_t freq;

    regBase = s_i2cRegBase[instance];
    i2cClockName = s_i2cClkName[instance];

    I2C_REG_GetBaudrate(regBase, &prescaler, &sampleWidth);

    (void)CLOCK_GetFreq(i2cClockName, &freq);

    *baudrate = freq / ((prescaler + 1) * (sampleWidth + 1));
}

/*******************************************************************************
 *** Function Name : I2C_MasterSetBaudRate
 *** Description   : Sets the baud rate(SCL frequency) for the I2C master
 ******************************************************************************/
errcode_t I2C_MasterSetBaudRate(uint32_t instance, i2c_baudrate_div_t baud)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    I2C_Type* regBase;
    errcode_t errCode = ERR_SUCCESS;
    i2c_master_state_t* master;

    regBase = s_i2cRegBase[instance];
    master = s_i2cMasterState[instance];

    ASSERT_PARAM(master != NULL);

    if (master->i2cIdle) {
        I2C_REG_SetBaudrate(regBase, baud.i2cBaudPrescaler, baud.i2cBaudSampleWidth);
        errCode = ERR_SUCCESS;
    } else {
        errCode = ERR_ERROR;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_MasterGetDefaultConfig
 *** Description   : Gets the default configuration structure for master
 ******************************************************************************/
void I2C_MasterGetDefaultConfig(i2c_master_config_t* config)
{
    ASSERT_PARAM(config != NULL);

    config->slaveAddress = 50U;
    config->is10bitAddr = false;
    config->baud.i2cBaudPrescaler = 5U;
    config->baud.i2cBaudSampleWidth = 9U;
    config->glitchFilterCnt = 0U;
    config->isMasterArb = false;
    config->isMasterSclSyc = false;
    config->transferType = I2C_USING_INTERRUPT;
    config->dmaChannel = 0U;
    config->masterCallback = NULL;
    config->callbackParam = NULL;
}

/*******************************************************************************
 *** Function Name : I2C_MasterSetSlaveAddr
 *** Description   : Sets the slave address which will be used for transfer
 ******************************************************************************/
void I2C_MasterSetSlaveAddr(uint32_t instance, uint16_t address, bool is10bitAddr)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    i2c_master_state_t* master;

    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    master = s_i2cMasterState[instance];
    ASSERT_PARAM(master != NULL);

    master->slaveAddress = address;
    master->is10bitAddr = is10bitAddr;
}

/*******************************************************************************
 *** Function Name : I2C_MasterSendData
 *** Description   : Starts the transmission of a block of data with no blocking
 ******************************************************************************/
errcode_t I2C_MasterSendData(uint32_t instance, const uint8_t* txBuff, uint32_t txSize, bool sendStop)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(txBuff != NULL);
    ASSERT_PARAM(txSize > 0U);

    I2C_Type* regBase;
    i2c_master_state_t* master;
    errcode_t errCode = ERR_SUCCESS;

    regBase = s_i2cRegBase[instance];
    master = s_i2cMasterState[instance];
    ASSERT_PARAM(master != NULL);

    /* Check if driver is busy */
    if (!master->i2cIdle) {
        errCode = ERR_BUSY;
    } else if ((txSize > DMA_CHANNEL_TRANFER_MAX_SIZE) && (master->transferType == I2C_USING_DMA)) {
        /* The transfer length must be less than 4096 with dma*/
        errCode = ERR_UNSUPPORTED;
    } else {
        /* Copy parameters to driver state structure */
        master->txBuffer = txBuff;
        master->txSize = txSize;
        master->sendStop = sendStop;
        master->i2cIdle = false;
        master->errCode = ERR_BUSY;

        if (master->transferType == I2C_USING_DMA) {
            /* Enable DMA Tx */
            I2C_REG_SetTxDmaEnable(regBase, true);
            /* Enable interrupt */
            I2C_REG_SetIntEnable(regBase, I2C_BYTE_TRANSFER_FINISH_INT
                                 | I2C_MASTER_ARBITRATION_LOST_INT
                                 | I2C_NACK_INT,
                                 true);
            I2C_MasterStartDmaTransfer(instance);
        } else {
            /* Enable interrupt */
            I2C_REG_SetIntEnable(regBase, I2C_BYTE_TRANSFER_FINISH_INT
                                 | I2C_MASTER_ARBITRATION_LOST_INT
                                 | I2C_NACK_INT,
                                 true);
        }

        /* Send start signal */
        errCode = I2C_SendStartSignal(regBase);

        if (errCode == ERR_SUCCESS) {
            errCode = I2C_MasterSendAddress(regBase, master, false);
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_MasterSendDataBlocking
 *** Description   : Starts the transmission of a block of data with blocking
 ******************************************************************************/
errcode_t I2C_MasterSendDataBlocking(uint32_t instance, const uint8_t* txBuff,
                                     uint32_t txSize, bool sendStop, uint32_t timeout)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(txBuff != NULL);
    ASSERT_PARAM(txSize > 0U);

    i2c_master_state_t* master;
    errcode_t errCode = ERR_SUCCESS;

    master = s_i2cMasterState[instance];
    ASSERT_PARAM(master != NULL);

    if (!master->i2cIdle) {
        errCode = ERR_BUSY;
    } else if ((txSize > DMA_CHANNEL_TRANFER_MAX_SIZE) && (master->transferType == I2C_USING_DMA)) {
        /* The transfer length must be less than 4096 with dma*/
        errCode = ERR_UNSUPPORTED;
    } else {
        master->blocking = true;

        /* Dummy wait to ensure the semaphore is 0, no need to check result */
        (void)OS_Adapter_WaitSemaphore(&(master->idleSemaphore), 0U);

        (void)I2C_MasterSendData(instance, txBuff, txSize, sendStop);
        errCode = I2C_MasterWaitTransferEnd(instance, timeout);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_MasterSendDataPolling
 *** Description   : Starts the transmission of a block of data with polling
 ******************************************************************************/
errcode_t I2C_MasterSendDataPolling(uint32_t instance, const uint8_t* txBuff,
                                    uint32_t txSize, bool sendStop)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(txBuff != NULL);
    ASSERT_PARAM(txSize > 0U);

    I2C_Type* regBase;
    i2c_master_state_t* master;
    errcode_t errCode = ERR_SUCCESS;
    uint8_t addrByte;
    uint32_t i;

    regBase = s_i2cRegBase[instance];
    master = s_i2cMasterState[instance];

    ASSERT_PARAM(master != NULL);

    /* Check if driver is busy */
    if (!master->i2cIdle) {
        errCode = ERR_BUSY;
    } else {
        /* Copy parameters to driver state structure */
        master->i2cIdle = false;

        /* Send start signal */
        errCode = I2C_SendStartSignal(regBase);

        if (errCode == ERR_SUCCESS) {
            if (master->is10bitAddr == false) {
                /* 7-bit addressing */
                /* Address byte: slave 7-bit address + D = 0(transmit) or 1 (receive) */
                addrByte = (uint8_t)(master->slaveAddress << 1U | (uint8_t)(0U));
                errCode = I2C_SendOneByteData(regBase, addrByte);
            } else {
                /* 10-bit addressing */
                /* First address byte: 1111 0XXD, where XX are bits 10 and 9 of address, and D = 0(transmit) */
                addrByte = (uint8_t)((uint8_t)0xF0U + (((master->slaveAddress >> 8U) << 1U) | (uint8_t)0U));
                errCode = I2C_SendOneByteData(regBase, addrByte);
                /* Second address byte: Remaining 8 bits of address: master->slaveAddress */
                errCode = I2C_SendOneByteData(regBase, (uint8_t)master->slaveAddress);
            }

            if (errCode == ERR_SUCCESS) {
                for (i = 0U; i < txSize; i++) {
                    errCode = I2C_SendOneByteData(regBase, txBuff[i]);
                    if (errCode != ERR_SUCCESS) {
                        if ((i == (txSize - 1)) && (errCode == ERR_I2C_RECEIVED_NACK)) {
                            errCode = ERR_SUCCESS;
                        } else {
                            break;
                        }
                    }
                }
            }

            if (sendStop) {
                (void)I2C_SendStopSignal(regBase);
            }

            master->i2cIdle = true;
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_MasterReceiveData
 *** Description   : Starts the reception of a block of data with no blocking
 ******************************************************************************/
errcode_t I2C_MasterReceiveData(uint32_t instance, uint8_t* rxBuff, uint32_t rxSize, bool sendStop)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(rxBuff != NULL);
    ASSERT_PARAM(rxSize > 0U);

    I2C_Type* regBase;
    i2c_master_state_t* master;
    errcode_t errCode = ERR_SUCCESS;

    regBase = s_i2cRegBase[instance];
    master = s_i2cMasterState[instance];
    ASSERT_PARAM(master != NULL);

    /* Check if driver is busy */
    if (!master->i2cIdle) {
        errCode = ERR_BUSY;
    } else if ((rxSize > DMA_CHANNEL_TRANFER_MAX_SIZE) && (master->transferType == I2C_USING_DMA)) {
        /* The transfer length must be less than 4096 with dma*/
        errCode = ERR_UNSUPPORTED;
    } else {
        /* Copy parameters to driver state structure */
        master->rxBuffer = rxBuff;
        master->rxSize = rxSize;
        master->i2cIdle = false;
        master->sendStop = sendStop;
        master->errCode = ERR_BUSY;

        if (master->transferType == I2C_USING_DMA) {
            /* Enable RX dma request */
            I2C_REG_SetRxDmaEnable(regBase, true);
            /* Enable interrupt */
            I2C_REG_SetIntEnable(regBase, I2C_BYTE_TRANSFER_FINISH_INT
                                 | I2C_MASTER_ARBITRATION_LOST_INT,
                                 true);
            I2C_MasterStartDmaTransfer(instance);
        } else {
            /* Enable interrupt */
            I2C_REG_SetIntEnable(regBase, I2C_BYTE_TRANSFER_FINISH_INT
                                 | I2C_MASTER_ARBITRATION_LOST_INT,
                                 true);
        }

        /* Send start signal */
        errCode = I2C_SendStartSignal(regBase);

        if (errCode == ERR_SUCCESS) {
            /* Send slave address */
            errCode = I2C_MasterSendAddress(regBase, master, true);
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_MasterReceiveDataBlocking
 *** Description   : Starts the reception of a block of data with blocking
 ******************************************************************************/
errcode_t I2C_MasterReceiveDataBlocking(uint32_t instance, uint8_t* rxBuff, uint32_t rxSize, bool sendStop, uint32_t timeout)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(rxBuff != NULL);
    ASSERT_PARAM(rxSize > 0U);

    i2c_master_state_t* master;
    errcode_t errCode = ERR_SUCCESS;

    master = s_i2cMasterState[instance];
    ASSERT_PARAM(master != NULL);

    if (!master->i2cIdle) {
        errCode = ERR_BUSY;
    } else if ((rxSize > DMA_CHANNEL_TRANFER_MAX_SIZE) && (master->transferType == I2C_USING_DMA)) {
        /* The transfer length must be less than 4096 with dma*/
        errCode = ERR_UNSUPPORTED;
    } else {
        master->blocking = true;

        /* Dummy wait to ensure the semaphore is 0, no need to check result */
        (void)OS_Adapter_WaitSemaphore(&(master->idleSemaphore), 0U);

        errCode = I2C_MasterReceiveData(instance, rxBuff, rxSize, sendStop);
        if (errCode == ERR_SUCCESS) {
            errCode = I2C_MasterWaitTransferEnd(instance, timeout);
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_MasterReceiveDataPolling
 *** Description   : Starts the reception of a block of data with no polling
 ******************************************************************************/
errcode_t I2C_MasterReceiveDataPolling(uint32_t instance, uint8_t* rxBuff, uint32_t rxSize, bool sendStop)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(rxBuff != NULL);
    ASSERT_PARAM(rxSize > 0U);

    I2C_Type* regBase;
    i2c_master_state_t* master;
    errcode_t errCode = ERR_SUCCESS;
    uint8_t addrByte;
    uint16_t i;

    regBase = s_i2cRegBase[instance];
    master = s_i2cMasterState[instance];

    ASSERT_PARAM(master != NULL);

    /* Check if driver is busy */
    if (!master->i2cIdle) {
        errCode = ERR_BUSY;
    } else {
        /* Copy parameters to driver state structure */
        master->i2cIdle = false;
        /* Send start signal */
        errCode = I2C_SendStartSignal(regBase);

        if (errCode == ERR_SUCCESS) {
            if (master->is10bitAddr == false) {
                /* 7-bit addressing */
                /* Address byte: slave 7-bit address + D = 0(transmit) or 1 (receive) */
                addrByte = (uint8_t)(master->slaveAddress << 1U | (uint8_t)1U);
                errCode = I2C_SendOneByteData(regBase, addrByte);
            } else {
                /* 10-bit addressing */
                /* First address byte: 1111 0XXD, where XX are bits 10 and 9 of address, and D = 0(transmit) */
                addrByte = (uint8_t)((uint8_t)0xF0U + (((master->slaveAddress >> 8U) << 1U) | (uint8_t)0U));
                errCode = I2C_SendOneByteData(regBase, addrByte);
                /* Second address byte: Remaining 8 bits of address: master->slaveAddress */
                errCode = I2C_SendOneByteData(regBase, (uint8_t)master->slaveAddress);
                /* Restart signal */
                errCode = I2C_SendStartSignal(regBase);
                /* Change direction */
                addrByte = (uint8_t)((uint8_t)0xF0U + (((master->slaveAddress >> 8U) << 1U) | (uint8_t)1U));
                (void)I2C_SendOneByteData(regBase, addrByte);
            }

            if (errCode == ERR_SUCCESS) {
                if (errCode == ERR_SUCCESS) {
                    if (rxSize == 1U) {
                        /* Start I2C master read */
                        (void)I2C_StartReceiveCmd(instance, I2C_NACK);
                        (void)I2C_ReceiveLastOneByteData(regBase, &rxBuff[0]);
                    } else {
                        /* Start I2C master read */
                        (void)I2C_StartReceiveCmd(instance, I2C_ACK);

                        for (i = 0; i < (rxSize - 2); i++) {
                            (void)I2C_ReceiveOneByteData(regBase, &rxBuff[i], I2C_ACK);
                        }

                        (void)I2C_ReceiveOneByteData(regBase, &rxBuff[i++], I2C_NACK);
                        (void)I2C_ReceiveLastOneByteData(regBase, &rxBuff[i]);
                    }
                }
            }
        }

        if (true != sendStop) {
            (void)I2C_SendStopSignal(regBase);
        }

        master->i2cIdle = true;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_MasterGetTransferStatus
 *** Description   : Return the current status of the I2C master transfer
 ******************************************************************************/
errcode_t I2C_MasterGetTransferStatus(uint32_t instance, uint32_t* byteRemaining)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    i2c_master_state_t* master;

    master = s_i2cMasterState[instance];

    ASSERT_PARAM(master != NULL);

    if ((master->transferType == I2C_USING_INTERRUPT) && (byteRemaining != NULL)) {
        if (master->txSize > 0U) {
            /* Send data */
            *byteRemaining = master->txSize;
        } else if (master->rxSize > 0U) {
            /* Receive data */
            *byteRemaining = master->rxSize;
        } else {
            *byteRemaining = 0U;
        }
    } else if ((master->transferType == I2C_USING_DMA) && (byteRemaining != NULL)) {
        if (s_i2cDmaTransferParam[instance].transferDirection == I2C_TX_REQ) {
            *byteRemaining = DMA_GetRemainingCount(master->dmaChannel);
        }
    } else {
        /* Nothing to do */
    }

    return master->errCode;
}

/*******************************************************************************
 *** Function Name : I2C_StartReceiveCmd
 *** Description   : Master start receive cmd
 ******************************************************************************/
errcode_t I2C_StartReceiveCmd(uint32_t instance, uint8_t nackType)
{
    I2C_Type* regBase;
    regBase = s_i2cRegBase[instance];
    volatile uint32_t timeout = I2C_HW_DEADLINE_TIMEOUT;
    errcode_t errCode = ERR_SUCCESS;

    I2C_REG_SetMasterTransmit(regBase, false);

    if (nackType == 0U) {
        /* response ACK */
        I2C_REG_RespondNack(regBase, false);
    } else {
        /* response NACK */
        I2C_REG_RespondNack(regBase, true);
    }

    I2C_REG_ReadCmd(regBase);

    do {
    } while (!(I2C_REG_GetStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG)) && (timeout-- > 0U));

    if (timeout == 0U) {
        errCode = ERR_TIMEOUT;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_MasterAbortTransferData
 *** Description   : Abort a non-blocking I2C Master transmission or reception
 ******************************************************************************/
void I2C_MasterAbortTransferData(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    I2C_Type* regBase;
    i2c_master_state_t* master;

    regBase = s_i2cRegBase[instance];
    master = s_i2cMasterState[instance];

    ASSERT_PARAM(master != NULL);

    master->errCode = ERR_I2C_ABORTED;
    I2C_MasterEndTransfer(regBase, master, true);
}

/*******************************************************************************
 *** Function Name : I2C_SlaveInit
 *** Description   : Initialize the I2C slave mode driver
 ******************************************************************************/
void I2C_SlaveInit(uint32_t instance, i2c_slave_state_t* slave, const i2c_slave_config_t* i2cConfig)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(i2cConfig != NULL);
    ASSERT_PARAM(slave != NULL);

    I2C_Type* regBase;
    errcode_t errCode = ERR_SUCCESS;
    module_names_t i2cModName;

    /* Check to see if the I2C slave instance is already initialized */
    ASSERT_PARAM(s_i2cSlaveState[instance] == NULL);

    regBase = s_i2cRegBase[instance];
    s_i2cSlaveState[instance] = slave;
    i2cModName = s_i2cModuleName[instance];

    /* Set i2c bus clock enable */
    CLOCK_SetModuleBusClockEnable(i2cModName, true);
    /* Set i2c instance in-active */
    CLOCK_SetModuleSoftResetState(i2cModName, RESET_INACTIVE);

    /* Initialize driver status structure */
    slave->rxBuffer = NULL;
    slave->rxSize = 0U;
    slave->txBuffer = NULL;
    slave->txSize = 0U;
    slave->errCode = ERR_SUCCESS;
    slave->is10bitAddr = i2cConfig->is10bitAddr;
    slave->transferType = i2cConfig->transferType;
    slave->isTransferInProgress = false;
    slave->callback = i2cConfig->slaveCallback;
    slave->callbackParam = i2cConfig->callbackParam;
    slave->blocking = false;
    slave->isSlaveStretch = i2cConfig->isSlaveStretch;
    slave->slaveListening = i2cConfig->slaveListening;

    /* Store DMA channel number used in transfer */
    slave->dmaChannel = i2cConfig->dmaChannel;

    /* Initialize the semaphore */
    errCode = OS_Adapter_CreateSemaphore(&(slave->idleSemaphore), 0U);
    ASSERT_PARAM(errCode == ERR_SUCCESS);
    (void)errCode;

    /* Enable i2c interrupt */
    NVIC_EnableIRQ(s_i2cIrqId[instance]);

    /* Initialize module */
    I2C_REG_SetMode(regBase, I2C_MODE_SLAVE);
    I2C_REG_SetDeglitchCnt(regBase, i2cConfig->glitchFilterCnt);
    I2C_REG_SetGeneralCallEnable(regBase, i2cConfig->isGeneralCall);
    I2C_REG_SetAlertEnable(regBase, i2cConfig->alertAddress);
    I2C_REG_SetAddressMode(regBase, i2cConfig->is10bitAddr);

    /* Set slave address */
    I2C_REG_SetSlaveAddress(regBase, i2cConfig->slaveAddress);

    if (i2cConfig->slaveListening) {
        I2C_REG_SetIntEnable(regBase, I2C_SLAVE_ADDRESS_MATCH_FLAG
                             | I2C_BYTE_TRANSFER_FINISH_FLAG
                             | I2C_SLAVE_TRANSMIT_UNDERFLOW_FLAG
                             | I2C_SLAVE_RECEIVE_OVERFLOW_FLAG
                             | I2C_NACK_FLAG
                             | I2C_STOP_FLAG,
                             true);

        /* Enable I2C module */
        I2C_REG_SetModuleEnable(regBase, true);
    }
    OS_Adapter_Init(NULL);
}

/*******************************************************************************
 *** Function Name : I2C_SlaveDeinit
 *** Description   : Deinitialize the I2C slave mode driver
 ******************************************************************************/
void I2C_SlaveDeinit(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    module_names_t i2cModNames;
    IRQn_Type i2cIrq;

    i2cModNames = s_i2cModuleName[instance];
    i2cIrq = s_i2cIrqId[instance];

    /* Set i2c module active */
    CLOCK_SetModuleSoftResetState(i2cModNames, RESET_ACTIVE);

    /* Destroy the semaphore */
    if (s_i2cSlaveState[instance] != NULL) {
        (void)OS_Adapter_DestroySemaphore(&(s_i2cSlaveState[instance]->idleSemaphore));
    }

    /* Disable i2c module clock and irq*/
    CLOCK_SetModuleBusClockEnable(i2cModNames, false);
    NVIC_DisableIRQ(i2cIrq);
    NVIC_ClearPendingIRQ(i2cIrq);

    s_i2cSlaveState[instance] = NULL;
}

/*******************************************************************************
 *** Function Name : I2C_SlaveSendData
 *** Description   : Starts the transmission of a block of data with no blocking
 ******************************************************************************/
errcode_t I2C_SlaveSendData(uint32_t instance, const uint8_t* txBuff, uint32_t txSize)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(txBuff != NULL);
    ASSERT_PARAM(txSize > 0U);

    I2C_Type* regBase;
    i2c_slave_state_t* slave;
    errcode_t errCode = ERR_SUCCESS;

    regBase = s_i2cRegBase[instance];
    slave = s_i2cSlaveState[instance];
    ASSERT_PARAM(slave != NULL);
    /* If the slave is in listening mode, it should not use the follwoing mode  */
    ASSERT_PARAM(slave->slaveListening == false);

    /* Check if slave is busy */
    if (slave->isTransferInProgress) {
        errCode = ERR_BUSY;
    } else if ((txSize > DMA_CHANNEL_TRANFER_MAX_SIZE) && (slave->transferType == I2C_USING_DMA)) {
        /* The transfer length must be less than 4096 with dma*/
        errCode = ERR_UNSUPPORTED;
    } else {
        slave->txBuffer = txBuff;
        slave->txSize = txSize;
        slave->errCode = ERR_BUSY;
        I2C_REG_SetStretchEnable(regBase, slave->isSlaveStretch);

        if (slave->transferType == I2C_USING_DMA) {
            I2C_REG_SetIntEnable(regBase, I2C_SLAVE_ADDRESS_MATCH_FLAG
                                 | I2C_BYTE_TRANSFER_FINISH_FLAG
                                 | I2C_SLAVE_TRANSMIT_UNDERFLOW_FLAG
                                 | I2C_NACK_FLAG
                                 | I2C_STOP_FLAG,
                                 true);
            /* Enable DMA Tx */
            I2C_REG_SetTxDmaEnable(regBase, true);
            /* Enable I2C module */
            I2C_REG_SetModuleEnable(regBase, true);
            slave->isTransferInProgress = true;
            I2C_SlaveStartDmaTransfer(instance);
        } else {
            I2C_REG_SetIntEnable(regBase, I2C_SLAVE_ADDRESS_MATCH_FLAG
                                 | I2C_BYTE_TRANSFER_FINISH_FLAG
                                 | I2C_SLAVE_TRANSMIT_UNDERFLOW_FLAG
                                 | I2C_NACK_FLAG
                                 | I2C_STOP_FLAG,
                                 true);

            /* Enable I2C module */
            I2C_REG_SetModuleEnable(regBase, true);

            slave->isTransferInProgress = true;
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_SlaveSendDataBlocking
 *** Description   : Starts the transmission of a block of data with blocking
 ******************************************************************************/
errcode_t I2C_SlaveSendDataBlocking(uint32_t instance, const uint8_t* txBuff, uint32_t txSize, uint32_t timeout)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(txBuff != NULL);
    ASSERT_PARAM(txSize > 0U);

    i2c_slave_state_t* slave;
    errcode_t errCode = ERR_SUCCESS;

    slave = s_i2cSlaveState[instance];

    ASSERT_PARAM(slave != NULL);

    /* Check if slave is busy */
    if (slave->isTransferInProgress) {
        errCode = ERR_BUSY;
    } else if ((txSize > DMA_CHANNEL_TRANFER_MAX_SIZE) && (slave->transferType == I2C_USING_DMA)) {
        /* The transfer length must be less than 4096 with dma*/
        errCode = ERR_UNSUPPORTED;
    } else {
        slave->blocking = true;
        /* Dummy wait to ensure the semaphore is 0, no need to check result */
        (void)OS_Adapter_WaitSemaphore(&(slave->idleSemaphore), 0U);

        (void)I2C_SlaveSendData(instance, txBuff, txSize);

        errCode = I2C_SlaveWaitTransferEnd(instance, timeout);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_SlaveReceiveData
 *** Description   : Starts the reception of a block of data with no blocking
 ******************************************************************************/
errcode_t I2C_SlaveReceiveData(uint32_t instance, uint8_t* rxBuff, uint32_t rxSize)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(rxBuff != NULL);
    ASSERT_PARAM(rxSize > 0U);

    I2C_Type* regBase;
    i2c_slave_state_t* slave;
    errcode_t errCode = ERR_SUCCESS;

    regBase = s_i2cRegBase[instance];
    slave = s_i2cSlaveState[instance];

    ASSERT_PARAM(slave != NULL);
    /* If the slave is in listening mode, it should not use the follwoing mode  */
    ASSERT_PARAM(slave->slaveListening == false);

    /* Check if slave is busy */
    if (slave->isTransferInProgress) {
        errCode = ERR_BUSY;
    } else if ((rxSize > DMA_CHANNEL_TRANFER_MAX_SIZE) && (slave->transferType == I2C_USING_DMA)) {
        /* The transfer length must be less than 4096 with dma*/
        errCode = ERR_UNSUPPORTED;
    } else {
        slave->rxBuffer = rxBuff;
        slave->rxSize = rxSize;
        slave->errCode = ERR_BUSY;
        I2C_REG_SetStretchEnable(regBase, slave->isSlaveStretch);

        if (rxSize != 1U) {
            I2C_REG_RespondNack(regBase, false);
        } else {
            I2C_REG_RespondNack(regBase, true);
        }

        if (slave->transferType == I2C_USING_DMA) {
            I2C_REG_SetIntEnable(regBase, I2C_SLAVE_ADDRESS_MATCH_FLAG
                                 | I2C_BYTE_TRANSFER_FINISH_FLAG
                                 | I2C_SLAVE_RECEIVE_OVERFLOW_FLAG
                                 | I2C_STOP_FLAG,
                                 true);
            /* Enable DMA Rx */
            I2C_REG_SetRxDmaEnable(regBase, true);
            /* Enable I2C module */
            I2C_REG_SetModuleEnable(regBase, true);
            slave->isTransferInProgress = true;
            I2C_SlaveStartDmaTransfer(instance);
        } else {
            slave->isTransferInProgress = true;
            I2C_REG_SetIntEnable(regBase, I2C_SLAVE_ADDRESS_MATCH_FLAG
                                 | I2C_BYTE_TRANSFER_FINISH_FLAG
                                 | I2C_SLAVE_RECEIVE_OVERFLOW_FLAG
                                 | I2C_STOP_FLAG,
                                 true);
            /* Enable I2C module */
            I2C_REG_SetModuleEnable(regBase, true);
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_SlaveReceiveDataBlocking
 *** Description   : Starts the reception of a block of data with blocking
 ******************************************************************************/
errcode_t I2C_SlaveReceiveDataBlocking(uint32_t instance, uint8_t* rxBuff, uint32_t rxSize, uint32_t timeout)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(rxBuff != NULL);
    ASSERT_PARAM(rxSize > 0U);

    i2c_slave_state_t* slave;
    errcode_t errCode = ERR_SUCCESS;

    slave = s_i2cSlaveState[instance];
    ASSERT_PARAM(slave != NULL);

    /* Check if slave is busy */
    if (slave->isTransferInProgress) {
        errCode = ERR_BUSY;
    } else if ((rxSize > DMA_CHANNEL_TRANFER_MAX_SIZE) && (slave->transferType == I2C_USING_DMA)) {
        /* The transfer length must be less than 4096 with dma*/
        errCode = ERR_UNSUPPORTED;
    } else {
        slave->blocking = true;
        /* Dummy wait to ensure the semaphore is 0, no need to check result */
        (void)OS_Adapter_WaitSemaphore(&(slave->idleSemaphore), 0U);

        (void)I2C_SlaveReceiveData(instance, rxBuff, rxSize);

        /* Wait for transfer to end */
        errCode = I2C_SlaveWaitTransferEnd(instance, timeout);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_SlaveSetRxBuffer
 *** Description   : Provide a buffer for receiving data
 ******************************************************************************/
errcode_t I2C_SlaveSetRxBuffer(uint32_t instance, uint8_t* rxBuff, uint32_t rxSize)
{
    ASSERT_PARAM(instance != CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(rxBuff != NULL);
    ASSERT_PARAM(rxSize > 0);
    i2c_slave_state_t* slave;

    slave = s_i2cSlaveState[instance];
    ASSERT_PARAM(slave != NULL);

    slave->rxBuffer = rxBuff;
    slave->rxSize = rxSize;

    return ERR_SUCCESS;
}

/*******************************************************************************
 *** Function Name : I2C_SlaveSetTxBuffer
 *** Description   : Provide a buffer for transmitting data
 ******************************************************************************/
errcode_t I2C_SlaveSetTxBuffer(uint32_t instance, uint8_t* txBuff, uint32_t txSize)
{
    ASSERT_PARAM(instance != CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(txBuff != NULL);
    ASSERT_PARAM(txSize > 0);
    i2c_slave_state_t* slave;

    slave = s_i2cSlaveState[instance];
    ASSERT_PARAM(slave != NULL);

    slave->txBuffer = txBuff;
    slave->txSize = txSize;

    return ERR_SUCCESS;
}

/*******************************************************************************
 *** Function Name : I2C_SlaveGetTransferStatus
 *** Description   : Return the current status of the I2C slave transfer
 ******************************************************************************/
errcode_t I2C_SlaveGetTransferStatus(uint32_t instance, uint32_t* byteRemaining)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    i2c_slave_state_t* slave;

    slave = s_i2cSlaveState[instance];

    ASSERT_PARAM(slave != NULL);

    if ((byteRemaining != NULL) && (slave->transferType == I2C_USING_INTERRUPT)) {
        if (slave->txSize > 0U) {
            /* Send data */
            *byteRemaining = slave->txSize;
        } else if (slave->rxSize > 0U) {
            *byteRemaining = slave->rxSize;
        } else {
            *byteRemaining = 0U;
        }
    } else {


    }

    return slave->errCode;
}

/*******************************************************************************
 *** Function Name : I2C_SlaveAbortTransferData
 *** Description   : Abort a non-blocking I2C Slave transmission or reception
 ******************************************************************************/
void I2C_SlaveAbortTransferData(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    i2c_slave_state_t* slave;
    I2C_Type* regBase;

    regBase = s_i2cRegBase[instance];
    slave = s_i2cSlaveState[instance];

    ASSERT_PARAM(slave != NULL);

    slave->errCode = ERR_I2C_ABORTED;
    I2C_SlaveEndTransfer(regBase, slave);
}

/*******************************************************************************
 *** Function Name : I2C_SlaveGetDefaultConfig
 *** Description   : Gets the default configuration structure for slave
 ******************************************************************************/
void I2C_SlaveGetDefaultConfig(i2c_slave_config_t* config)
{
    ASSERT_PARAM(config != NULL);

    config->slaveAddress = 50U;
    config->is10bitAddr = false;
    config->alertAddress = false;
    config->isGeneralCall = false;
    config->isSlaveStretch = false;
    config->glitchFilterCnt = 0U;
    config->transferType = I2C_USING_INTERRUPT;
    config->dmaChannel = 0U;
    config->slaveCallback = NULL;
    config->callbackParam = NULL;
    config->slaveListening = true;
}

/*******************************************************************************
 *** Function Name : I2C_SetBusTimeoutDetection
 *** Description   : Set bus low timeout detection
 ******************************************************************************/
errcode_t I2C_SetBusTimeoutDetection(uint32_t instance, i2c_bus_timeout_t mode, uint16_t threshold)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    I2C_Type* regBase;
    i2c_master_state_t* master;
    i2c_slave_state_t* slave;
    errcode_t errCode = ERR_SUCCESS;

    regBase = s_i2cRegBase[instance];
    master = s_i2cMasterState[instance];
    slave = s_i2cSlaveState[instance];

    if ((master == NULL) && (slave == NULL)) {
        errCode = ERR_ERROR;
    } else if (master != NULL) {
        if (master->i2cIdle != true) {
            errCode = ERR_BUSY;
        }
    } else {
        if (slave->isTransferInProgress == true) {
            errCode = ERR_BUSY;
        }
    }

    if (errCode == ERR_SUCCESS) {
        switch (mode) {
            case I2C_BUS_TIMEOUT_DISABLE:
                I2C_REG_SetLowTimeoutThreshold(regBase, 0U);
                break;

            case I2C_BUS_TIMEOUT_SCL:
                I2C_REG_SetLowTimeoutMode(regBase, 0U);
                I2C_REG_SetLowTimeoutThreshold(regBase, threshold);
                break;

            case I2C_BUS_TIMEOUT_SCL_SDA:
                I2C_REG_SetLowTimeoutMode(regBase, 1U);
                I2C_REG_SetLowTimeoutThreshold(regBase, threshold);
                break;

            default:
                break;
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_ResetHwTransferLogic
 *** Description   : Reset the hardware transfer logic
 ******************************************************************************/
void I2C_ResetHwTransferLogic(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    I2C_Type* regBase;

    regBase = s_i2cRegBase[instance];

    I2C_REG_SoftwareReset(regBase);
}

/*******************************************************************************
 *** Function Name : I2C_InstallCallback
 *** Description   : Register callback function and its parameter for i2c instance
 ******************************************************************************/
void I2C_InstallCallback(uint32_t instance, i2c_callback_t callback, void* param)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    if (s_i2cMasterState[instance] != NULL) {
        s_i2cMasterState[instance]->callback = callback;
        s_i2cMasterState[instance]->callbackParam = param;
    } else if (s_i2cSlaveState[instance] != NULL) {
        s_i2cSlaveState[instance]->callback = callback;
        s_i2cSlaveState[instance]->callbackParam = param;
    } else {
        /* Nothing to do */
    }
}

/*******************************************************************************
 *** Function Name : I2C_IRQHandlerProcess
 *** Description   : I2C interrupt implement
 ******************************************************************************/
void I2C_IRQHandlerProcess(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    I2C_Type* regBase;
    i2c_master_state_t* master;

    regBase = s_i2cRegBase[instance];
    master = s_i2cMasterState[instance];

    /* Check if i2c is master or slave */
    if ((I2C_REG_IsMasterMode(regBase)) || (((I2C_REG_IsMasterMode(regBase) == false) && (master != NULL)))) {
        /* I2C is master mode or slave mode but arbitation lost */
        I2C_MasterIRQProcess(instance);
    } else {
        I2C_SlaveIRQProcess(instance);
    }
}

/*******************************************************************************
 *** Function Name : I2C_CheckSlaveBuffer
 *** Description   : Check the buffer is ready or not.
                     This API only used in listening mode.
 ******************************************************************************/
static errcode_t I2C_CheckSlaveBuffer(i2c_slave_state_t* slave)
{
    ASSERT_PARAM(slave != NULL);
    errcode_t errCode = ERR_SUCCESS;

    if ((s_slaveDirection == I2C_SLAVE_RECEIVE) && (slave->rxBuffer == NULL)) {
        errCode = ERR_ERROR;
    } else if ((s_slaveDirection == I2C_SLAVE_TRANSMIT) && (slave->txBuffer == NULL)) {
        errCode = ERR_ERROR;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_MasterWaitTransferEnd
 *** Description   : Waits for the end of a blocking transfer
 ******************************************************************************/
static errcode_t I2C_MasterWaitTransferEnd(uint32_t instance, uint32_t timeout)
{
    I2C_Type* regBase;
    errcode_t errCode = ERR_SUCCESS;
    i2c_master_state_t* master;

    regBase = s_i2cRegBase[instance];
    master = s_i2cMasterState[instance];

    ASSERT_PARAM(master != NULL);

    /* Wait for transfer to be completed by the IRQ */
    errCode = OS_Adapter_WaitSemaphore(&(master->idleSemaphore), timeout);
    I2C_REG_SetIntEnable(regBase, I2C_ALL_INTERRUPT, false);

    if (errCode == ERR_TIMEOUT) {
        I2C_MasterEndTransfer(regBase, master, true);
        master->errCode = ERR_TIMEOUT;
    }

    master->blocking = false;

    return master->errCode;
}

/*******************************************************************************
 *** Function Name : I2C_MasterStartDmaTransfer
 *** Description   : Starts the DMA transfer for master
 ******************************************************************************/
static void I2C_MasterStartDmaTransfer(uint32_t instance)
{
    I2C_Type* regBase = s_i2cRegBase[instance];
    i2c_master_state_t* master = s_i2cMasterState[instance];
    i2c_dma_transfer_t* dmaTransferParams = &s_i2cDmaTransferParam[instance];
    uint32_t txSize;
    const uint8_t* txBuffer;
    uint32_t rxSize;
    uint8_t* rxBuffer;

    ASSERT_PARAM(master != NULL);

    dmaTransferParams->dmaChannel = master->dmaChannel;
    DMA_InstallCallback(dmaTransferParams->dmaChannel, I2C_MasterDmaCallback, (void*)instance);

    if (master->txSize > 0U) {
        /* Send Data */
        txBuffer = master->txBuffer;
        txSize = master->txSize;
        master->txSize = 0U;

        dmaTransferParams->dmaTransferType = DMA_TRANSFER_MEM2PERIPH;
        dmaTransferParams->i2cDataRegAddr = (uint32_t)(&(regBase->DATA));
        dmaTransferParams->bufferAddress = (uint32_t)txBuffer;
        dmaTransferParams->transferDirection = I2C_TX_REQ;
        dmaTransferParams->transferSize = txSize;
        dmaTransferParams->request = s_i2cTxDmaRequest[instance];
    } else {
        /* Receive Data */
        rxBuffer = master->rxBuffer;
        I2C_REG_SetNumberOfByteResponseNack(regBase, master->rxSize);
        rxSize = master->rxSize;
        master->rxSize = 0U;

        dmaTransferParams->dmaTransferType = DMA_TRANSFER_PERIPH2MEM;
        dmaTransferParams->i2cDataRegAddr = (uint32_t)(&(regBase->DATA));
        dmaTransferParams->bufferAddress = (uint32_t)rxBuffer;
        dmaTransferParams->transferDirection = I2C_RX_REQ;
        dmaTransferParams->transferSize = rxSize;
        dmaTransferParams->request = s_i2cRxDmaRequest[instance];
    }

    (void)I2C_ConfigureDmaTransfer(instance, dmaTransferParams);
    DMA_ConfigureInt(master->dmaChannel, DMA_CHN_COMPLETE_INT, true);

    /* Start dma channel */
    (void)DMA_StartChannel(dmaTransferParams->dmaChannel);
}

/*******************************************************************************
 *** Function Name : I2C_MasterSendAddress
 *** Description   : Master Send Address
 ******************************************************************************/
static errcode_t I2C_MasterSendAddress(I2C_Type* regBase, i2c_master_state_t* master, bool receive)
{
    uint8_t addrByte;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t interruptRecord;

    ASSERT_PARAM(master != NULL);

    if (master->is10bitAddr) {
        /* Disable dma and interrupt */
        interruptRecord = I2C_REG_GetIntEnableInfo(regBase);
        I2C_REG_SetIntEnable(regBase, I2C_ALL_INTERRUPT, false);
        I2C_REG_SetTxDmaEnable(regBase, false);
        I2C_REG_SetRxDmaEnable(regBase, false);

        /* 10-bit addressing */
        /* First address byte: 1111 0XXD, where XX are bits 10 and 9 of address, and D = 0(transmit) */
        addrByte = (uint8_t)(0xF0U + ((master->slaveAddress >> 7U) & 0x6U) + (uint8_t)0U);
        errCode = I2C_SendOneByteData(regBase, addrByte);

        if ((!receive) && (errCode == ERR_SUCCESS)) {
            /* Resume interrupt and dma */
            I2C_REG_SetIntEnable(regBase, interruptRecord, true);
            I2C_REG_SetTxDmaEnable(regBase, (bool)master->transferType);

            /* Second address byte: Remaining 8 bits of address */
            /* Subsequent data transmission is handled by interrupts */
            addrByte = (uint8_t)(master->slaveAddress & 0xFFU);
            I2C_REG_SetMasterTransmit(regBase, true);
            I2C_REG_SendData(regBase, addrByte);
        } else if ((receive) && (errCode == ERR_SUCCESS)) {
            /* Second address byte: Remaining 8 bits of address */
            addrByte = (uint8_t)(master->slaveAddress & 0xFFU);
            errCode = I2C_SendOneByteData(regBase, addrByte);

            /* Send Restart signal and change transfer direction */
            I2C_REG_ClearStatus(regBase, I2C_START_FLAG);
            (void)I2C_SendStartSignal(regBase);

            /* Receiving from 10-bit slave - must send repeated start and resend first address byte */
            /* First address byte: 1111 0XXD, where XX are bits 10 and 9 of address, and D = 1 (receive) */
            /* Subsequent data transmission is handled by interrupts */
            addrByte = (uint8_t)(0xF0U + ((master->slaveAddress >> 7U) & 0x6U) + (uint8_t)1U);

            if (master->transferType == I2C_USING_INTERRUPT) {
                /* Resume interrupt */
                I2C_REG_SetIntEnable(regBase, interruptRecord, true);
                I2C_REG_SendData(regBase, addrByte);
            } else {
                errCode = I2C_SendOneByteData(regBase, addrByte);
                /* Resume interrupt and dma */
                I2C_REG_SetIntEnable(regBase, interruptRecord, true);
                I2C_REG_SetRxDmaEnable(regBase, true);
                I2C_REG_SetMasterTransmit(regBase, false);
                I2C_REG_ReadCmd(regBase);

            }
        } else {
            /* nothing to do */
        }

        if (errCode != ERR_SUCCESS) {
            (void)I2C_SendStopSignal(regBase);
        }
    } else {
        /* 7-bit addressing */
        /* Address byte: slave 7-bit address + D = 0(transmit) or 1 (receive) */
        addrByte = (uint8_t)((master->slaveAddress << 1U) + (uint8_t)receive);
        I2C_REG_SetMasterTransmit(regBase, true);

        if (master->transferType == I2C_USING_DMA && receive) {
            interruptRecord = I2C_REG_GetIntEnableInfo(regBase);
            I2C_REG_SetIntEnable(regBase, I2C_ALL_INTERRUPT, false);
            (void)I2C_SendOneByteData(regBase, addrByte);
            I2C_REG_SetIntEnable(regBase, interruptRecord, true);
            I2C_REG_SetMasterTransmit(regBase, false);
            I2C_REG_ReadCmd(regBase);
        } else {
            I2C_REG_SendData(regBase, addrByte);
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_MasterEndTransfer
 *** Description   : Ends current transmission or reception
 ******************************************************************************/
static void I2C_MasterEndTransfer(I2C_Type* regBase,
                                  i2c_master_state_t* master,
                                  bool sendStop)
{
    ASSERT_PARAM(master != NULL);
    ASSERT_PARAM(regBase != NULL);

    /* Disable all interrupt */
    I2C_REG_SetIntEnable(regBase, I2C_ALL_INTERRUPT, false);

    if (master->transferType == I2C_USING_DMA) {
        /* Stop DMA channel and inactivate interrupts */
        (void)DMA_StopChannel(master->dmaChannel);

        /* Disable I2C DMA request */
        I2C_REG_SetTxDmaEnable(regBase, false);
        I2C_REG_SetRxDmaEnable(regBase, false);
    }

    if (sendStop) {
        (void)I2C_SendStopSignal(regBase);
    }

    master->txBuffer = NULL;
    master->rxBuffer = NULL;
    master->txSize = 0U;
    master->rxSize = 0U;
    master->i2cIdle = true;
}

/*******************************************************************************
 *** Function Name : I2C_MasterHandleTransmitData
 *** Description   : Handle master transmit data
 ******************************************************************************/
static void I2C_MasterHandleTransmitData(uint32_t instance, i2c_master_state_t* master)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(master != NULL);

    I2C_Type* regBase = s_i2cRegBase[instance];

    if (master->txSize == 0U) {
        /* There is no more data in buffer, the transmission is over */
        I2C_REG_ClearStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG | I2C_NACK_FLAG);

        I2C_MasterIntEndTransfer(instance, master, ERR_SUCCESS, I2C_EVENT_END_TRANSFER);
    } else {
        /* Transmission is in progress */
        I2C_REG_SendData(regBase, *(master->txBuffer));
        master->txBuffer++;
        master->txSize--;
    }
}

/*******************************************************************************
 *** Function Name : I2C_MasterHandleReceiveData
 *** Description   : Handle master receive data in interrupt
 ******************************************************************************/
static void I2C_MasterHandleReceiveData(uint32_t instance, i2c_master_state_t* master)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(master != NULL);
    ASSERT_PARAM(master->rxBuffer != NULL);

    I2C_Type* regBase = s_i2cRegBase[instance];

    /* Received data to user buffer */
    if (master->rxSize == 0U) {
        /* avoid generate next clock */
        I2C_REG_SetMasterTransmit(regBase, true);

        *(master->rxBuffer) = I2C_REG_ReceiveData(regBase);
        I2C_REG_ClearStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG);

        I2C_MasterIntEndTransfer(instance, master, ERR_SUCCESS, I2C_EVENT_END_TRANSFER);
    } else if (master->rxSize == 1U) {
        I2C_REG_RespondNack(regBase, true);
        *(master->rxBuffer) = I2C_REG_ReceiveData(regBase);
        master->rxSize--;
        master->rxBuffer++;
    } else {
        I2C_REG_RespondNack(regBase, false);
        *(master->rxBuffer) = I2C_REG_ReceiveData(regBase);
        master->rxSize--;
        master->rxBuffer++;
    }
}

/*******************************************************************************
 *** Function Name : I2C_MasterIntEndTransfer
 *** Description   : Ends current transmission or reception in interrupt
 ******************************************************************************/
static void I2C_MasterIntEndTransfer(uint32_t instance, i2c_master_state_t* master,
                                     errcode_t errCode, i2c_event_t event)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(master != NULL);

    I2C_Type* regBase = s_i2cRegBase[instance];

    master->errCode = errCode;

    I2C_MasterEndTransfer(regBase, master, master->sendStop);

    if (master->callback != NULL) {
        master->callback(instance, event, master->callbackParam);
    }

    /* Signal transfer end for blocking transfers */
    if (master->blocking == true) {
        (void)OS_Adapter_PostSemaphore(&(master->idleSemaphore));
    }
}

/*******************************************************************************
 *** Function Name : I2C_MasterIRQProcess
 *** Description   : Handle master operation when I2C interrupt occurs
 ******************************************************************************/
static void I2C_MasterIRQProcess(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    I2C_Type* regBase;
    i2c_master_state_t* master;

    regBase = s_i2cRegBase[instance];
    master = s_i2cMasterState[instance];

    ASSERT_PARAM(master != NULL);

    if ((I2C_REG_GetStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG)) &&
            (!I2C_REG_IsTxDmaEnable(regBase)) &&
            (!I2C_REG_IsRxDmaEnable(regBase))) {
        /* Byte transfer finished */

        if (I2C_REG_GetStatus(regBase, I2C_NACK_FLAG) && (master->txSize != 0)) {

            /* End of transmit */
            I2C_MasterIntEndTransfer(instance, master, ERR_I2C_RECEIVED_NACK, I2C_EVENT_END_TRANSFER);
            /* Clear NACK flag & byte BTF*/
            I2C_REG_ClearStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG | I2C_NACK_FLAG);
        } else {
            if ((I2C_REG_GetMasterTransmitDir(regBase) == I2C_MASTER_TRANSMIT) && (master->rxBuffer == NULL)) {
                /* I2C transmit data */
                I2C_MasterHandleTransmitData(instance, master);
            } else if ((I2C_REG_GetMasterTransmitDir(regBase) == I2C_MASTER_TRANSMIT) && (master->rxBuffer != NULL)) {
                /* Slave address send complete, I2C master change transfer direction */
                I2C_REG_SetMasterTransmit(regBase, false);

                if (master->rxSize == 1U) {
                    I2C_REG_RespondNack(regBase, true);
                } else {
                    I2C_REG_RespondNack(regBase, false);
                }

                I2C_REG_ClearStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG | I2C_NACK_FLAG);
                master->rxSize--;
                /* Start I2C master read */
                I2C_REG_ReadCmd(regBase);
            } else {
                I2C_MasterHandleReceiveData(instance, master);
            }
        }
    }

    if (I2C_REG_IsRxDmaEnable(regBase) || I2C_REG_IsTxDmaEnable(regBase)) {
        if (I2C_REG_GetStatus(regBase, I2C_NACK_FLAG) && (master->errCode != ERR_SUCCESS)) {
            /* DMA transfer NACK is recevied,transfer is end*/
            I2C_MasterIntEndTransfer(instance, master, ERR_I2C_RECEIVED_NACK, I2C_EVENT_END_TRANSFER);
            I2C_REG_ClearStatus(regBase, I2C_NACK_FLAG | I2C_BYTE_TRANSFER_FINISH_FLAG);
        } else if (I2C_REG_GetStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG)) {
            /* DMA transfer last byte transfer finished , transfer is end*/
            I2C_REG_ClearStatus(regBase, I2C_NACK_FLAG | I2C_BYTE_TRANSFER_FINISH_FLAG);
            I2C_MasterIntEndTransfer(instance, master, ERR_SUCCESS, I2C_EVENT_END_TRANSFER);
        }
    }

    if (I2C_REG_GetStatus(regBase, I2C_MASTER_ARBITRATION_LOST_FLAG)) {
        /* Clear arbtiration lost flag */
        I2C_REG_ClearStatus(regBase, I2C_MASTER_ARBITRATION_LOST_FLAG);

        /* End of transmit */
        I2C_MasterIntEndTransfer(instance, master, ERR_I2C_ARBITRATION_LOST, I2C_EVENT_END_TRANSFER);
    }

    if (I2C_REG_GetStatus(regBase, I2C_LOW_TIMEOUT_DETECTED_FLAG)) {
        /* Clear low timeout flag */
        I2C_REG_ClearStatus(regBase, I2C_LOW_TIMEOUT_DETECTED_FLAG);

        if (master->callback != NULL) {
            master->callback(instance, I2C_EVENT_LOW_TIMEOUT, master->callbackParam);
        }
    }
}

/*******************************************************************************
 *** Function Name : I2C_ConfigureDmaTransfer
 *** Description   : I2C configure Dma transfer Parameter
 ******************************************************************************/
static void I2C_ConfigureDmaTransfer(uint32_t instance, i2c_dma_transfer_t* dmaTransferParams)
{
    if (dmaTransferParams->transferDirection == I2C_TX_REQ) {
        (void)DMA_ConfigSingleTransfer(dmaTransferParams->dmaChannel, dmaTransferParams->dmaTransferType,
                                       (uint32_t)dmaTransferParams->bufferAddress,
                                       (uint32_t)dmaTransferParams->i2cDataRegAddr, DMA_TRANSFER_WIDTH_8BIT,
                                       (uint32_t)dmaTransferParams->transferSize, dmaTransferParams->request);
    } else {
        (void)DMA_ConfigSingleTransfer(dmaTransferParams->dmaChannel, dmaTransferParams->dmaTransferType,
                                       (uint32_t)dmaTransferParams->i2cDataRegAddr,
                                       (uint32_t)dmaTransferParams->bufferAddress, DMA_TRANSFER_WIDTH_8BIT,
                                       (uint32_t)dmaTransferParams->transferSize, dmaTransferParams->request);
    }
}

/*******************************************************************************
 *** Function Name : I2C_SendStartSignal
 *** Description   : Master Send Start signal
 ******************************************************************************/
static errcode_t I2C_SendStartSignal(I2C_Type* regBase)
{
    ASSERT_PARAM(regBase != NULL);

    uint32_t timeout = I2C_HW_DEADLINE_TIMEOUT;
    errcode_t errCode = ERR_SUCCESS;

    I2C_REG_StartCmd(regBase);

    while ((timeout-- > 0U) && (!I2C_REG_GetStatus(regBase, I2C_START_FLAG)))
    {}

    /* Clear start signal flag */
    I2C_REG_ClearStatus(regBase, I2C_START_FLAG);

    if (timeout == 0U) {
        errCode = ERR_TIMEOUT;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_SendOneByteData
 *** Description   : Send one byte data
 ******************************************************************************/
static errcode_t I2C_SendOneByteData(I2C_Type* regBase, uint8_t data)
{
    ASSERT_PARAM(regBase != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t flag = 0U;
    uint32_t timeout = I2C_HW_DEADLINE_TIMEOUT;

    I2C_REG_SetMasterTransmit(regBase, true);
    I2C_REG_SendData(regBase, data);

    do {
        flag = I2C_REG_GetStatus(regBase, I2C_ALL_FLAG);
        timeout--;
    } while ((!(flag & I2C_BYTE_TRANSFER_FINISH_FLAG)) && (timeout > 0U));

    if (flag & I2C_BYTE_TRANSFER_FINISH_FLAG) {
        I2C_REG_ClearStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG);
        errCode = ERR_SUCCESS;
    }

    if (flag & I2C_NACK_FLAG) {
        I2C_REG_ClearStatus(regBase, I2C_NACK_FLAG);
        errCode = ERR_I2C_RECEIVED_NACK;
    }

    if (timeout == 0U) {
        errCode = ERR_TIMEOUT;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_SendStopSignal
 *** Description   : Master Send Stop signal
 ******************************************************************************/
static errcode_t I2C_SendStopSignal(I2C_Type* regBase)
{
    ASSERT_PARAM(regBase != NULL);

    uint32_t timeout = I2C_HW_DEADLINE_TIMEOUT;
    errcode_t errCode = ERR_SUCCESS;

    while ((!I2C_REG_GetMasterReady(regBase)) && (timeout > 0U)) {
        timeout--;
    }

    if (timeout == 0U) {
        errCode = ERR_TIMEOUT;
    }

    timeout = I2C_HW_DEADLINE_TIMEOUT;

    I2C_REG_StopCmd(regBase);
    while ((!I2C_REG_GetStopFlag(regBase)) && (timeout > 0U)) {
        timeout--;
    }

    /* Clear stop signal flag */
    I2C_REG_ClearStatus(regBase, I2C_STOP_FLAG);

    if (timeout == 0U) {
        errCode = ERR_TIMEOUT;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_ReceiveOneByteData
 *** Description   : Send one byte data
 ******************************************************************************/
static errcode_t I2C_ReceiveOneByteData(I2C_Type* regBase, uint8_t* data, uint8_t nackType)
{
    ASSERT_PARAM(regBase != NULL);
    errcode_t errCode = ERR_SUCCESS;
    volatile uint32_t timeout = I2C_HW_DEADLINE_TIMEOUT;

    if (nackType == 0U) {
        /* response ACK */
        I2C_REG_RespondNack(regBase, false);
    } else {
        /* response NACK */
        I2C_REG_RespondNack(regBase, true);
    }

    *data = I2C_REG_ReceiveData(regBase);

    do {
    } while (!(I2C_REG_GetStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG)) && (timeout-- > 0U));

    if (timeout == 0U) {
        errCode = ERR_TIMEOUT;
    } else {
        do {
            I2C_REG_ClearStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG);
        } while (I2C_REG_GetStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG));
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_ReceiveLastOneByteData
 *** Description   : Send one byte data
 ******************************************************************************/
static errcode_t I2C_ReceiveLastOneByteData(I2C_Type* regBase, uint8_t* data)
{
    ASSERT_PARAM(regBase != NULL);

    errcode_t errCode = ERR_SUCCESS;
    I2C_REG_SetMasterTransmit(regBase, true);
    I2C_REG_ClearStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG);
    *data = I2C_REG_ReceiveData(regBase);

    return errCode;
}

/*******************************************************************************
 *** Function Name : I2C_SlaveStartDmaTransfer
 *** Description   : starts the DMA transfer for slave
 ******************************************************************************/
static void I2C_SlaveStartDmaTransfer(uint32_t instance)
{
    I2C_Type* regBase = s_i2cRegBase[instance];
    i2c_slave_state_t* slave = s_i2cSlaveState[instance];
    i2c_dma_transfer_t* dmaTransferParams = &s_i2cDmaTransferParam[instance];
    const uint8_t* txBuffer;
    uint32_t txSize;
    uint8_t* rxBuffer;
    uint32_t rxSize;

    ASSERT_PARAM(slave != NULL);

    if (slave->txSize > 0U) {
        /* Send Data */
        txBuffer = slave->txBuffer;
        /* Data can be transferred by DMA all at once */
        txSize = slave->txSize;
        slave->txSize = 0U;

        dmaTransferParams->dmaChannel = slave->dmaChannel;
        dmaTransferParams->dmaTransferType = DMA_TRANSFER_MEM2PERIPH;
        dmaTransferParams->i2cDataRegAddr = (uint32_t)(&(regBase->DATA));
        dmaTransferParams->bufferAddress = (uint32_t)txBuffer;
        dmaTransferParams->transferDirection = I2C_TX_REQ;
        dmaTransferParams->transferSize = txSize;
        dmaTransferParams->request = s_i2cTxDmaRequest[instance];
    } else {
        /* Receive Data */
        rxBuffer = slave->rxBuffer;
        I2C_REG_SetNumberOfByteResponseNack(regBase, slave->rxSize);
        rxSize = slave->rxSize;
        slave->rxSize = 0U;

        dmaTransferParams->dmaChannel = slave->dmaChannel;
        dmaTransferParams->dmaTransferType = DMA_TRANSFER_PERIPH2MEM;
        dmaTransferParams->i2cDataRegAddr = (uint32_t)(&(regBase->DATA));
        dmaTransferParams->bufferAddress = (uint32_t)rxBuffer;
        dmaTransferParams->transferDirection = I2C_RX_REQ;
        dmaTransferParams->transferSize = rxSize;
        dmaTransferParams->request = s_i2cRxDmaRequest[instance];
    }

    DMA_InstallCallback(slave->dmaChannel, I2C_SlaveDmaCallback, (void*)instance);
    I2C_ConfigureDmaTransfer(instance, dmaTransferParams);
    DMA_ConfigureInt(slave->dmaChannel, DMA_CHN_COMPLETE_INT, true);

    /* Start channel */
    DMA_StartChannel(dmaTransferParams->dmaChannel);
}

/*******************************************************************************
 *** Function Name : I2C_SlaveWaitTransferEnd
 *** Description   : Waits for the end of a blocking transfer
 ******************************************************************************/
static errcode_t I2C_SlaveWaitTransferEnd(uint32_t instance, uint32_t timeout)
{
    errcode_t errCode = ERR_SUCCESS;
    I2C_Type* regBase;
    i2c_slave_state_t* slave;

    regBase = s_i2cRegBase[instance];
    slave = s_i2cSlaveState[instance];

    ASSERT_PARAM(slave != NULL);

    /* Wait for transfer to be completed by the IRQ */
    errCode = OS_Adapter_WaitSemaphore(&(slave->idleSemaphore), timeout);

    if (errCode == ERR_TIMEOUT) {
        I2C_SlaveEndTransfer(regBase, slave);
        slave->errCode = ERR_TIMEOUT;
    }

    slave->blocking = false;
    return slave->errCode;
}

/*******************************************************************************
 *** Function Name : I2C_SlaveEndTransfer
 *** Description   : ends current transmission or reception
 ******************************************************************************/
static void I2C_SlaveEndTransfer(I2C_Type* regBase, i2c_slave_state_t* slave)
{
    ASSERT_PARAM(regBase != NULL);
    ASSERT_PARAM(slave != NULL);

    I2C_REG_SetIntEnable(regBase, I2C_ALL_FLAG, false);

    if (slave->transferType == I2C_USING_DMA) {
        (void)DMA_StopChannel(slave->dmaChannel);

        /* Disable the DMA request */
        I2C_REG_SetTxDmaEnable(regBase, false);
        I2C_REG_SetRxDmaEnable(regBase, false);
    }

    if (slave->isSlaveStretch) {
        I2C_REG_SetStretchEnable(regBase, false);
    }

    /* Disable I2C module */
    I2C_REG_SetModuleEnable(regBase, false);

    slave->isTransferInProgress = false;
    slave->rxBuffer = NULL;
    slave->rxSize = 0U;
    slave->txBuffer = NULL;
    slave->txSize = 0U;
    s_slaveDirection = I2C_SLAVE_RECEIVE;
}

/*******************************************************************************
 *** Function Name : I2C_SlaveIRQProcess
 *** Description   : Handle slave operation when I2C interrupt occurs
 ******************************************************************************/
static void I2C_SlaveIRQProcess(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);

    I2C_Type* regBase;
    i2c_slave_state_t* slave;

    regBase = s_i2cRegBase[instance];
    slave = s_i2cSlaveState[instance];

    ASSERT_PARAM(slave != NULL);

    if (!I2C_REG_IsTxDmaEnable(regBase) && !I2C_REG_IsRxDmaEnable(regBase)) {
        if (I2C_REG_GetStatus(regBase, I2C_SLAVE_ADDRESS_MATCH_FLAG)) {
            /* Slave address match, Record the transmission direction */
            s_slaveDirection = I2C_REG_GetSlaveDirection(regBase);
            I2C_REG_ClearStatus(regBase, I2C_SLAVE_ADDRESS_MATCH_FLAG);

            if (s_slaveDirection == I2C_SLAVE_TRANSMIT) {
                if (slave->slaveListening) {
                    if (slave->callback != NULL) {
                        slave->callback(instance, I2C_SLAVE_EVENT_TX_REQ, slave->callbackParam);
                        if (I2C_CheckSlaveBuffer(slave) != ERR_SUCCESS) {
                            slave->callback(instance, I2C_EVENT_BUFFER_INVALID, slave->callbackParam);
                        } else {
                            I2C_SlaveHandleTransmitData(instance, slave);
                        }
                    }
                } else {
                    I2C_SlaveHandleTransmitData(instance, slave);
                }

            } else {
                if ((slave->callback != NULL) && slave->slaveListening) {
                    slave->callback(instance, I2C_SLAVE_EVENT_RX_REQ, slave->callbackParam);
                }
                (slave->rxSize == 1U) ? I2C_REG_RespondNack(regBase, true) : I2C_REG_RespondNack(regBase, false);
                slave->rxSize--;
            }
        } else if (I2C_REG_GetStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG) || I2C_REG_GetStatus(regBase, I2C_NACK_FLAG)) {
            /* Byte transfer finish */
            I2C_REG_ClearStatus(regBase, I2C_NACK_FLAG | I2C_BYTE_TRANSFER_FINISH_FLAG);
            if (I2C_REG_GetStatus(regBase, I2C_NACK_FLAG) && (slave->txSize != 0)) {
                I2C_SlaveIntEndTransfer(instance, slave, ERR_I2C_RECEIVED_NACK, I2C_EVENT_END_TRANSFER);
            } else {
                if (s_slaveDirection == I2C_SLAVE_TRANSMIT) {
                    I2C_SlaveHandleTransmitData(instance, slave);
                } else {
                    I2C_SlaveHandleReceiveData(instance, slave);
                }
            }
        } else {
            /* Nothing to do */
        }
    } else {
        if (I2C_REG_GetStatus(regBase, I2C_SLAVE_ADDRESS_MATCH_FLAG)) {

            I2C_REG_ClearStatus(regBase, I2C_SLAVE_ADDRESS_MATCH_FLAG);
        }

        if (I2C_REG_GetStatus(regBase, I2C_NACK_FLAG)) {
            if (slave->errCode != ERR_SUCCESS) {
                I2C_SlaveIntEndTransfer(instance, slave, ERR_I2C_RECEIVED_NACK, I2C_EVENT_END_TRANSFER);
            }

            I2C_REG_ClearStatus(regBase, I2C_NACK_FLAG | I2C_BYTE_TRANSFER_FINISH_FLAG);
        }
    }

    if (I2C_REG_GetStatus(regBase, I2C_SLAVE_TRANSMIT_UNDERFLOW_FLAG)) {
        /* transmit data underflow */

        I2C_REG_ClearStatus(regBase, I2C_SLAVE_TRANSMIT_UNDERFLOW_FLAG);

        I2C_SlaveIntEndTransfer(instance, slave, ERR_I2C_TX_UNDERRUN, I2C_SLAVE_EVENT_TX_UNDERFLOW);
    }

    if (I2C_REG_GetStatus(regBase, I2C_SLAVE_RECEIVE_OVERFLOW_FLAG)) {
        /* Receive data overflow */

        I2C_REG_ClearStatus(regBase, I2C_SLAVE_RECEIVE_OVERFLOW_FLAG);

        I2C_SlaveIntEndTransfer(instance, slave, ERR_I2C_RX_OVERRUN, I2C_SLAVE_EVENT_RX_OVERFLOW);
    }

    if (I2C_REG_GetStatus(regBase, I2C_LOW_TIMEOUT_DETECTED_FLAG)) {
        /* Clear low timeout flag */
        I2C_REG_ClearStatus(regBase, I2C_LOW_TIMEOUT_DETECTED_FLAG);

        if (slave->callback != NULL) {
            slave->callback(instance, I2C_EVENT_LOW_TIMEOUT, slave->callbackParam);
        }
    }

    if (I2C_REG_GetStatus(regBase, I2C_STOP_FLAG)) {
        /* Stop signal interrupt handler */
        I2C_REG_ClearStatus(regBase, I2C_STOP_FLAG);

        if (slave->errCode == ERR_BUSY) {
            if (s_slaveDirection == I2C_SLAVE_TRANSMIT) {
                /* Avoid the slave from sending the last interrupt before the processing is complete,
                and the host stop has been sent */
                I2C_SlaveHandleTransmitData(instance, slave);
            }

            if (slave->errCode == ERR_BUSY) {
                /* Slave transfer is not finished */
                slave->errCode = ERR_ERROR;
            }
        }
        I2C_SlaveIntEndTransfer(instance, slave, slave->errCode, I2C_EVENT_STOP);

    }
}

/*******************************************************************************
 *** Function Name : I2C_SlaveHandleTransmitData
 *** Description   : Handle slave transmit data in interrupt
 ******************************************************************************/
static void I2C_SlaveHandleTransmitData(uint32_t instance, i2c_slave_state_t* slave)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(slave != NULL);

    I2C_Type* regBase = s_i2cRegBase[instance];

    if (slave->txBuffer != NULL) {
        if (slave->txSize == 0U) {
            /* There is no more data in buffer, the transmission is over */
            /* Clear BTF flag */
            I2C_REG_ClearStatus(regBase, I2C_BYTE_TRANSFER_FINISH_FLAG | I2C_NACK_FLAG);

            /* Disable stetch to avoid slave pull down I2C bus */
            if (slave->isSlaveStretch) {
                I2C_REG_SetStretchEnable(regBase, false);
            }

            /* End the current transfer */
            I2C_SlaveIntEndTransfer(instance, slave, ERR_SUCCESS, I2C_EVENT_END_TRANSFER);

        } else {
            I2C_REG_SendData(regBase, *(slave->txBuffer));
            slave->txSize--;
            slave->txBuffer++;
        }
    }
}

/*******************************************************************************
 *** Function Name : I2C_SlaveHandleReceiveData
 *** Description   : Handle slave transmit data in interrupt
 ******************************************************************************/
static void I2C_SlaveHandleReceiveData(uint32_t instance, i2c_slave_state_t* slave)
{
    ASSERT_PARAM(instance < CONFIG_I2C_INSTANCE_COUNT);
    ASSERT_PARAM(slave != NULL);

    I2C_Type* regBase = s_i2cRegBase[instance];

    if (slave->rxBuffer != NULL) {
        /* Received data to user buffer */
        if (slave->rxSize == 0U) {
            *(slave->rxBuffer) = I2C_REG_ReceiveData(regBase);
            if (!slave->slaveListening) {
                /* Disable stetch to avoid slave pull down I2C bus */
                if (slave->isSlaveStretch) {
                    I2C_REG_SetStretchEnable(regBase, false);
                }
            }
            /* End the current transfer */
            I2C_SlaveIntEndTransfer(instance, slave, ERR_SUCCESS, I2C_EVENT_END_TRANSFER);
        } else if (slave->rxSize == 1U) {
            *(slave->rxBuffer) = I2C_REG_ReceiveData(regBase);
            slave->rxSize--;
            slave->rxBuffer++;
            I2C_REG_RespondNack(regBase, true);
        } else {
            I2C_REG_RespondNack(regBase, false);
            *(slave->rxBuffer) = I2C_REG_ReceiveData(regBase);
            slave->rxSize--;
            slave->rxBuffer++;
        }
    }
}

/*******************************************************************************
 *** Function Name : I2C_SlaveIntEndTransfer
 *** Description   : Ends current transmission or reception in interrupt
 ******************************************************************************/
static void I2C_SlaveIntEndTransfer(uint32_t instance, i2c_slave_state_t* slave,
                                    errcode_t errCode, i2c_event_t event)
{
    ASSERT_PARAM(slave != NULL);

    I2C_Type* regBase = s_i2cRegBase[instance];

    if (event == I2C_EVENT_STOP) {
        if (!slave->slaveListening) {
            /* Signal transfer end for blocking transfers */
            if (slave->blocking == true) {
                (void)OS_Adapter_PostSemaphore(&(slave->idleSemaphore));
            }

            I2C_SlaveEndTransfer(regBase, slave);
        } else {
            slave->rxBuffer = NULL;
            slave->rxSize = 0U;
            slave->txBuffer = NULL;
            slave->txSize = 0U;
        }

    } else {
        slave->errCode = errCode;
    }

    if (slave->callback != NULL) {
        slave->callback(instance, event, slave->callbackParam);
    }
}

/*******************************************************************************
 *** Function Name : I2C_MasterDmaCallback
 *** Description   : I2C Master dma callback function
 ******************************************************************************/
static void I2C_MasterDmaCallback(uint8_t channel, dma_event_t event, void* param)
{
    (void)channel;
    uint32_t instance = ((uint32_t)param);
    i2c_master_state_t* master = s_i2cMasterState[instance];
    I2C_Type* regBase = s_i2cRegBase[instance];

    if (event == DMA_EVENT_TRANFER_COMPLETE) {
        if (I2C_REG_GetMasterTransmitDir(regBase) == I2C_MASTER_TRANSMIT) {
            master->errCode = ERR_SUCCESS;
        } else {
            I2C_MasterIntEndTransfer(instance, master, ERR_SUCCESS, I2C_EVENT_END_TRANSFER);
        }
    } else {
        master->errCode = ERR_ERROR;
        I2C_REG_ClearStatus(regBase, I2C_ALL_FLAG);
        (void)I2C_SendStopSignal(regBase);

        if (master->callback) {
            master->callback(instance, I2C_EVENT_DMA_TRANSFER_ERROR, master->callbackParam);
        }
    }
}

/*******************************************************************************
 *** Function Name : I2C_SlaveDmaCallback
 *** Description   : I2C Slave dma callback function
 ******************************************************************************/
static void I2C_SlaveDmaCallback(uint8_t channel, dma_event_t event, void* param)
{
    (void)channel;
    uint32_t instance = ((uint32_t)param);
    i2c_slave_state_t* slave = s_i2cSlaveState[instance];
    I2C_Type* regBase = s_i2cRegBase[instance];

    if (event == DMA_EVENT_TRANFER_COMPLETE) {
        /* Disable stetch to avoid slave pull down I2C bus */
        if (slave->isSlaveStretch) {
            I2C_REG_SetStretchEnable(regBase, false);
        }

        I2C_SlaveIntEndTransfer(instance, slave, ERR_SUCCESS, I2C_EVENT_END_TRANSFER);
    }
}

/*******EOF********************************************************************/
